feat(plugins): apply config edits live without a display restart (4 plugins)#166
feat(plugins): apply config edits live without a display restart (4 plugins)#166rpierce99 wants to merge 1 commit into
Conversation
📝 WalkthroughWalkthroughAdds ChangesLive Config Hot-Reload Across Plugins
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Complexity | 20 |
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
Four installed plugins had no on_config_change override, so the base BasePlugin only swapped self.config and their derived settings (and the components that captured config at construction) kept startup values until the display service was restarted. This adds on_config_change to each so the 2s config-file watcher applies edits immediately: - ledmatrix-flights: re-derive scalars and rebuild the data-source fetcher, route enrichment and renderer; invalidate the cached map so a new center/radius/zoom re-tiles. - ledmatrix-elections: re-derive filters, rebuild providers + race store, reconfigure the scroll helper, and force a re-fetch. - baseball-scoreboard / football-scoreboard: re-derive league/display settings and rebuild the per-league managers (cleaning up old HTTP sessions first), league registry, scroll manager and rotation modes so favorite teams, league enable/disable, durations and live priority apply live. Per-game progress tracking is reset since manager keys may change. Each plugin gains a regression test that fails against the base behavior and passes with the override. Versions bumped + plugins.json synced. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
6397855 to
16e965b
Compare
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@plugins/baseball-scoreboard/manager.py`:
- Around line 278-279: The inconsistency between how self.enabled and
self.is_enabled are initialized during config reloads can unintentionally
re-enable runtime behavior on partial config updates. Line 278 correctly
preserves the prior state of self.enabled when the "enabled" key is missing by
using getattr(self, "enabled", True) as a fallback, but line 279 hard-defaults
self.is_enabled to True instead. Fix this by updating line 279 to follow the
same pattern as line 278: use getattr(self, "is_enabled", True) as the fallback
value instead of just True, so that self.is_enabled also preserves its prior
state when the config key is omitted.
In `@plugins/football-scoreboard/manifest.json`:
- Line 4: The version string in the get_info() method in manager.py is out of
sync with the updated version in manifest.json. Update the version value
returned by the get_info() method from "2.1.1" to "2.4.0" to ensure the
runtime-reported plugin version matches the manifest version and maintains
consistency for diagnostics and UI consumers.
In `@plugins/ledmatrix-flights/manager.py`:
- Around line 409-410: The configuration update for tracked_flights_cfg does not
clean up stale entries from the runtime tracked_flight_data dictionary when
flights are removed from the config during hot-reload. After updating
tracked_flights_cfg on line 409, add logic to identify and remove any entries
from tracked_flight_data that no longer exist in the updated configuration by
comparing the tracked flight identifiers between the old and new config values.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 688b6c86-75df-497a-8430-7717abc641cb
📒 Files selected for processing (16)
plugins.jsonplugins/baseball-scoreboard/manager.pyplugins/baseball-scoreboard/manifest.jsonplugins/baseball-scoreboard/test_config_reload.pyplugins/clock-simple/manager.pyplugins/clock-simple/manifest.jsonplugins/clock-simple/test_config_reload.pyplugins/football-scoreboard/manager.pyplugins/football-scoreboard/manifest.jsonplugins/football-scoreboard/test_config_reload.pyplugins/ledmatrix-elections/manager.pyplugins/ledmatrix-elections/manifest.jsonplugins/ledmatrix-elections/test_config_reload.pyplugins/ledmatrix-flights/manager.pyplugins/ledmatrix-flights/manifest.jsonplugins/ledmatrix-flights/test_config_reload.py
| self.enabled = self.config.get("enabled", getattr(self, "enabled", True)) | ||
| self.is_enabled = self.config.get("enabled", True) |
There was a problem hiding this comment.
Keep runtime enable flags consistent during partial config reloads.
Line 278 preserves prior state when enabled is omitted, but Line 279 hard-defaults to True. That can unintentionally re-enable runtime behavior on partial config payloads.
Suggested fix
- self.enabled = self.config.get("enabled", getattr(self, "enabled", True))
- self.is_enabled = self.config.get("enabled", True)
+ enabled_flag = self.config.get("enabled", getattr(self, "is_enabled", True))
+ self.enabled = enabled_flag
+ self.is_enabled = enabled_flag🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@plugins/baseball-scoreboard/manager.py` around lines 278 - 279, The
inconsistency between how self.enabled and self.is_enabled are initialized
during config reloads can unintentionally re-enable runtime behavior on partial
config updates. Line 278 correctly preserves the prior state of self.enabled
when the "enabled" key is missing by using getattr(self, "enabled", True) as a
fallback, but line 279 hard-defaults self.is_enabled to True instead. Fix this
by updating line 279 to follow the same pattern as line 278: use getattr(self,
"is_enabled", True) as the fallback value instead of just True, so that
self.is_enabled also preserves its prior state when the config key is omitted.
| "id": "football-scoreboard", | ||
| "name": "Football Scoreboard", | ||
| "version": "2.3.5", | ||
| "version": "2.4.0", |
There was a problem hiding this comment.
Sync runtime-reported plugin version with manifest version.
Line 4 bumps this manifest to 2.4.0, but plugins/football-scoreboard/manager.py Line 2428 still reports "version": "2.1.1" in get_info(). This creates a cross-file metadata contract mismatch for diagnostics/UI/registry consumers.
🔧 Proposed fix
--- a/plugins/football-scoreboard/manager.py
+++ b/plugins/football-scoreboard/manager.py
@@
- "version": "2.1.1",
+ "version": "2.4.0",🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@plugins/football-scoreboard/manifest.json` at line 4, The version string in
the get_info() method in manager.py is out of sync with the updated version in
manifest.json. Update the version value returned by the get_info() method from
"2.1.1" to "2.4.0" to ensure the runtime-reported plugin version matches the
manifest version and maintains consistency for diagnostics and UI consumers.
| self.tracked_flights_cfg = self.config.get('tracked_flights', []) | ||
| self.anchor_airport = self.config.get('anchor_airport', '') |
There was a problem hiding this comment.
Reconcile tracked-flight runtime state when config changes.
Line 409 updates tracked_flights_cfg, but removed/replaced tracked identifiers are never pruned from tracked_flight_data. After hot-reload, stale tracked flights can keep rendering even though they were removed from config.
💡 Proposed fix
self.tracked_flights_cfg = self.config.get('tracked_flights', [])
self.anchor_airport = self.config.get('anchor_airport', '')
self.route_cache_ttl = self.config.get('route_cache_ttl', 300)
+
+ # Reconcile tracked-flight state with the new config so removed identifiers
+ # do not keep rendering after live reload.
+ configured_tracked = set(self.tracked_flights_cfg[:3])
+ for ident in list(self.tracked_flight_data.keys()):
+ if ident not in configured_tracked:
+ self.tracked_flight_data.pop(ident, None)
+ if self._tracking_index >= max(1, len(self.tracked_flight_data)):
+ self._tracking_index = 0🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@plugins/ledmatrix-flights/manager.py` around lines 409 - 410, The
configuration update for tracked_flights_cfg does not clean up stale entries
from the runtime tracked_flight_data dictionary when flights are removed from
the config during hot-reload. After updating tracked_flights_cfg on line 409,
add logic to identify and remove any entries from tracked_flight_data that no
longer exist in the updated configuration by comparing the tracked flight
identifiers between the old and new config values.
Problem
Most plugin settings only took effect after restarting the display service. The core already hot-reloads
config.json(a 2s file-watcher inConfigServicenotifiesBasePlugin.on_config_change), but the baseon_config_changeonly swapsself.config— it doesn't re-derive a plugin's settings, and it can't rebuild the components a plugin builds from config at construction. So editing a setting in the web UI appeared to do nothing until a restart.Most first-party plugins never override
on_config_change. This adds it to four of them so edits apply live.What each plugin now refreshes live
configat construction) and invalidates the cached map background so a new center/radius/zoom re-tiles. Runtime state (tracked aircraft, trails, API counters, records, proximity state machine) is left untouched.Genuinely hardware-level settings (panel geometry, etc.) are intentionally not made live — those still warrant a clean restart.
Tests
Each plugin gets a
test_config_reload.pythat instantiates the plugin with stub managers, callson_config_change, and asserts the derived settings/components update. Each was verified to fail against the base behavior and pass with the override.check_plugin.py) against all four at every size — no crashes/overflow, all PASS.ledmatrix-electionssuites still pass (72 + 61).Notes
Versions bumped (minor) and
plugins.jsonsynced. The same one-method pattern extends to the remaining plugins in follow-ups. (clock-simplewas prepared too but is held back for a separate PR: it has a pre-existing 64×32 / 64×64 overflow — unrelated to config reload — that the safety harness flags, and that should be fixed on its own.)